﻿using CKS.Core.Framework.Models;
using CKS.PLC.Core.Event;
using McProtocolNext;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using S7.Net;
using SqlSugar.IOC;
using SqlSugar;
using System.Diagnostics;
using ZR.Model.Models.ProductionControl;
using Infrastructure;
using CKS.PLC.Core.Alarm;
using CKS.Core.Framework;

namespace CKS.PLC.Core.Core
{
    public class McClient : IPlcClient
    {
        public PlcConfig PlcInfo { get; set; }
        public bool IsConnected { get; set; }
        public bool IsOnline { get; set; }
        IServiceProvider serviceProvider;
        ILogger<McClient> logger;
        IFreeSql<MesDB> fsql = null;

        public McProtocol plcSession;
        int boolLenth = 160;
        public bool?[] alarmBools = null;

        public McClient(PlcConfig _config, IServiceProvider _serviceProvider)
        {
            this.PlcInfo = _config;
            serviceProvider = _serviceProvider;
            logger = serviceProvider.GetService<ILogger<McClient>>();
            fsql = serviceProvider.GetService<IFreeSql<MesDB>>();
            //初始化报警状态
            alarmBools = new bool?[boolLenth];
            for (int i = 0; i < alarmBools.Length; i++) { alarmBools[i] = false; }
        }

        public async Task ConnectPlc(CancellationToken cancellation)
        {
            try
            {
                if (!plcSession.CheckPlcConnection())
                {
                    await plcSession.TryReconnectToPlcAsync(cancellation);
                }
                if (IsConnected == false && plcSession.CheckPlcConnection())
                {
                    EventBus.AddLog($"{PlcInfo.WorkstationCode}:{plcSession.Configuration.Ip} 连接成功");
                }
                IsConnected = plcSession.CheckPlcConnection();
                if(IsConnected == false&& 
                    (
                        !PlcInfo.PlcAddress.Equals(plcSession.Configuration.Ip)||
                        !PlcInfo.PlcPort.Equals(plcSession.Configuration.Port)
                    ) )
                {
                    UpdatePlcSession();
                }
            }
            catch (Exception ex)
            {
                if (IsConnected)
                {
                    EventBus.AddLog($"{PlcInfo.WorkstationCode}:{plcSession.Configuration.Ip} 连接已断开");
                }
                //logger.LogWarning($"plc:{plcSession.IP}连接异常 {ex.Message}");
                IsConnected = false;
                if (!PlcInfo.PlcAddress.Equals(plcSession.Configuration.Ip))
                {
                    EventBus.AddLog($"{PlcInfo.WorkstationCode} PLC/IP变化");
                    UpdatePlcSession();
                }
                Debug.WriteLine($"{PlcInfo.WorkstationCode} 连接失败 {DateTime.Now.ToString("HH:mm:ss.fff")}"); ;
            }
        }

        public void DoHeart()
        {

        }

        public async Task Execute(CancellationToken cancellation)
        {
            try
            {
                while (!cancellation.IsCancellationRequested)
                {
                    //检查当前PLC状态是否禁用 
                    //检查当前PLC的通信点位是否有变化，有变化需要重新加载
                    int STEP = 1;
                    try
                    {
                        //EventBus.AddLog($"{plcConfig.WorkstationCode} MySql: {db.ContextID}");
                        var model = fsql.Select<PlcConfig>().Where(a => a.Id == PlcInfo.Id).First();
                        if (model == null || model.IsEnabled == false)
                        {
                            await Task.Delay(1000, cancellation);
                            continue;
                        }
                        PlcInfo.IsEnabled = model.IsEnabled;
                        PlcInfo.PlcAddress = model.PlcAddress;
                        PlcInfo.PlcPort = model.PlcPort;
                        STEP = 2;
                        await ConnectPlc(cancellation);
                        STEP = 3;
                        await ExecuteMain();
                    }
                    catch (Exception ex)
                    {

                        EventBus.AddLog($"{PlcInfo.WorkstationCode} STEP:{STEP} 异常{ex.GetType()}: {ex.Message}");
                        if (ex is SqlSugarException || ex.Source == "MySqlConnector")
                        {
                            EventBus.AddLog($"{PlcInfo.WorkstationCode} STEP:{STEP} 数据库访问异常，重置数据库对象");
                        }
                    }


                    await Task.Delay(30, cancellation);
                }
                await Task.CompletedTask;
            }
            catch (Exception ex)
            {
                logger.LogWarning($"异常 {ex.Message}");
            }


        }



    
        public DateTimeOffset LastProccessDeviceStatusTime { get; set; } = AppCache.MinTime.ToUniversalTime();
        public async Task ExecuteMain()
        {
            string errmsg = "";
            try
            {
                if (!plcSession.CheckPlcConnection())
                { return; }
                //获取需要读取的数据块
              
                var bytes = await plcSession.ReadDeviceBlockAsync(PlcDeviceType.D, 9500, 20);
                var wordLength = 10;
                var words = new short[wordLength];
                OP150_HC_DeviceStatus_Action doDeviceStatus = new OP150_HC_DeviceStatus_Action();
                for (int i = 0; i < wordLength; i++)
                {
                    // words[i] = (short)(bytes[i * 2] | (bytes[i * 2 + 1]) << 8);
                    words[i] = BitConverter.ToInt16(bytes, i * 2);
                    
                }
                var runState = words[2];
                var deviceMode = words[4];
                //每间隔1秒调用1次
                doDeviceStatus.Proccess(this, runState,deviceMode);

                var boolBytes = bytes.Skip(10 * 2).ToArray();
               
                bool?[] bits = new bool?[boolLenth];
                for (int i = 0; i < boolLenth; i++)
                {
                    bits[i] = (boolBytes[i / 8] >> (i % 8) & 1) == 1;
                }
                //处理心跳信号
                var heartValue = words[0];
                ProcessHeart(heartValue);

                //处理设备状态OEE
                //处理设备报警
                OP150_HC_Alarm_Action doAlarm = new OP150_HC_Alarm_Action();
                for (int i = 0; i <= boolLenth; i++)
                {
                    if (bits[i] != alarmBools[i])
                    {
                        //调用报警操作类 
                        doAlarm.Process(i + 1, bits[i].Value, alarmBools[i]);
                    }
                    alarmBools[i] = bits[i];
                }
            }
            catch (Exception ex)
            {
                logger.LogWarning($"工站:{PlcInfo.WorkstationCode}异常，{errmsg}, {ex.Message} {ex.StackTrace}");
                await Task.Delay(1000);

            }
        }
        short LastHeart = -1;
        private DateTime LastHeartTime = App.MinTime;
        int HeartSeed = 0;
        bool LastConnected = false;
        private void ProcessHeart(short plcHeartValue)
        {
            try
            {
                var ts = DateTime.Now - LastHeartTime;
                if (ts.TotalSeconds < 5)
                {
                    IsOnline = true;
                    App.AddOnlinePlcs(PlcInfo.Id);
                }
                else
                {
                    IsOnline = false;
                    App.RemoveOnlinePlcs(PlcInfo.Id);
                }


                ts = DateTime.Now - LastHeartTime;
                if (ts.TotalMilliseconds > 500)
                {
                    HeartSeed++;
                    if (HeartSeed > 1000)
                        HeartSeed = 1;
                    Int16 valueToWrite = (short)(HeartSeed % 2);
                    //查询心跳点位
                   

                    if (LastHeart != plcHeartValue || IsConnected != LastConnected)
                    {
                        //logger.Information($"变更心跳值{plcHeartValue}");
                        // EventBus.PlcHeart(this, plcHeartValue);
                        LastHeartTime = DateTime.Now;
                        EventBus.PlcHeart(this, plcHeartValue);
                    }
                    LastHeart = plcHeartValue;
                    LastConnected = IsConnected;
                }
            }
            catch (Exception ex)
            {
                logger.LogWarning($"{PlcInfo.WorkstationCode} 心跳异常 {ex.Message}");
            }
        }

        public void ExecuteSubscriptionAction(DecodeValue value)
        {
        }

        public void ProccessPointValue(DecodeValue value)
        {
            
        }

        public int? ReadInt16(int db, int startByteAdr)
        {
            return 0;
        }

        public async Task StartAsync(CancellationToken cancellation)
        {
            await Task.Yield();

            try
            {
                UpdatePlcSession();
                BindCacheData();
                await Execute(cancellation);
                await Task.CompletedTask;
            }
            catch (Exception ex)
            {

            }
        }

        private void BindCacheData()
        {
            if (!PlcInfo.IsEnabled)
                return;
            //重置缓存
        }

        public void UpdatePlcSession()
        {

            McProtocolConfig config = new McProtocolConfig()
            {
                ProtocolFrame = "MC3E",
                Ip = PlcInfo.PlcAddress,
                Port = (short)PlcInfo.PlcPort
            };

            plcSession = new McProtocol(config);

        }
    }
}
